// 第 6 步：实现 log() 的格式化接口

#pragma once

#include "lf_queue.hpp"
#include "thread_utils.hpp"
#include "time_utils.h"
#include <cstdint>
#include <cstdio>
#include <string>
#include <thread>
#include <fstream>

namespace Common {
// step 1:
// 首先写好头文件的基本结构、依赖、命名空间，以及日志元素 LogElement 的定义。
// 它使用 enum class LogType 标记数据类型，内部通过 union 存放不同原生类型的值。
constexpr size_t LOG_QUEUE_SIZE = 8 * 1024 * 1024;

enum class LogType : int8_t {
  CHAR = 0,
  INTEGER,
  LONG_INTEGER,
  LONG_LONG_INTEGER,
  UNSIGNED_INTEGER,
  UNSIGNED_LONG_INTEGER,
  UNSIGNED_LONG_LONG_INTEGER,
  FLOAT,
  DOUBLE
};

struct LogElement {
  LogType type_ = LogType::CHAR;
  union {
    char c;
    int i;
    long l;
    long long ll;
    unsigned u;
    unsigned long ul;
    unsigned long long ull;
    float f;
    double d;
  } u_;
};

// step 2:
// 接着列出类的大致框架，包括构造、析构、后台线程逻辑 flushQueue()，以及核心成员变量（文件流、队列、线程指针等）
class Logger final {  
public:
  auto flushQueue() noexcept;
  explicit Logger(const std::string &file_name);
  ~Logger();

  // step 5:
  // 首先提供一个接受 LogElement 的通用版本，再给常见原生类型写重载（自动封装成 LogElement 存入队列）。对于字符串，循环写入每个字符。
  auto pushValue(const LogElement &log_element) noexcept;
  auto pushValue(char value) noexcept;
  auto pushValue(int value) noexcept;
  auto pushValue(long value) noexcept;
  auto pushValue(long long value) noexcept;
  auto pushValue(unsigned value) noexcept;
  auto pushValue(unsigned long value) noexcept;
  auto pushValue(unsigned long long value) noexcept;
  auto pushValue(float value) noexcept;
  auto pushValue(double value) noexcept;

  auto pushValue(const char *value) noexcept;
  auto pushValue(const std::string &value) noexcept ;

  // step 6:
  // 这个接口支持像 log("% %", 42, "ok") 一样的顺序替换。通过递归模板逐个消耗参数，遇到 % 就写入当前值，遇到 %% 写一个 %。
  template <typename T, typename... A>
  auto log(const char *s, const T &value, A... args) noexcept;

  auto log(const char *s) noexcept;

  Logger() = delete;
  Logger(const Logger &) = delete;
  Logger(const Logger &&) = delete;
  Logger &operator=(const Logger &) = delete;
  Logger &operator=(const Logger &&) = delete;

private:
  const std::string file_name_;
  std::ofstream file_;
  LFQueue<LogElement> queue_;
  std::atomic<bool> running_ = {true};
  std::thread *logger_thread_ = nullptr;
};

// step 3:
// flushQueue() 负责不停从无锁队列中取出元素写入文件。取完一批后 flush()，再稍作休眠，避免忙等。
auto Logger::flushQueue() noexcept {
  while (running_) {
    for (auto next = queue_.getNextToRead(); queue_.size() && next;
         next = queue_.getNextToRead()) {
      switch (next->type_) {
      case LogType::CHAR:
        file_ << next->u_.c;
        break;
      case LogType::INTEGER:
        file_ << next->u_.i;
        break;
      case LogType::LONG_INTEGER:
        file_ << next->u_.l;
        break;
      case LogType::LONG_LONG_INTEGER:
        file_ << next->u_.ll;
        break;
      case LogType::UNSIGNED_INTEGER:
        file_ << next->u_.u;
        break;
      case LogType::UNSIGNED_LONG_INTEGER:
        file_ << next->u_.ul;
        break;
      case LogType::UNSIGNED_LONG_LONG_INTEGER:
        file_ << next->u_.ull;
        break;
      case LogType::FLOAT:
        file_ << next->u_.f;
        break;
      case LogType::DOUBLE:
        file_ << next->u_.d;
        break;
      }
      queue_.updateReadIndex();
    }
    file_.flush();

    using namespace std::literals::chrono_literals;
    std::this_thread::sleep_for(10ms);
  }
}

// step 4:
// 构造函数要打开文件、创建后台线程；析构函数要等队列清空、停止线程并关闭文件，同时输出提示日志。
Logger::Logger(const std::string &file_name)
    : file_name_(file_name), queue_(LOG_QUEUE_SIZE) {
  file_.open(file_name);
  ASSERT(file_.is_open(), "Could not open log file:" + file_name);
  logger_thread_ = createAndStartThread(-1, "Common/Logger " + file_name_,
                                        [this]() { flushQueue(); });
  ASSERT(logger_thread_ != nullptr, "Failed to start Logger thread.");
}

Logger::~Logger() {
  std::string time_str;
  std::cerr << Common::getCurrentTimeStr(&time_str)
            << " Flushing and closing Logger for " << file_name_ << std::endl;

  while (queue_.size()) {
    using namespace std::literals::chrono_literals;
    std::this_thread::sleep_for(1s);
  }
  running_ = false;
  logger_thread_->join();

  file_.close();
  std::cerr << Common::getCurrentTimeStr(&time_str) << " Logger for "
            << file_name_ << " exiting." << std::endl;
}

// step 5:
// 首先提供一个接受 LogElement 的通用版本，再给常见原生类型写重载（自动封装成 LogElement 存入队列）。对于字符串，循环写入每个字符。
auto Logger::pushValue(const LogElement &log_element) noexcept {
  *(queue_.getNextToWriteTo()) = log_element;
  queue_.updateWriteIndex();
}

auto Logger::pushValue(const char value) noexcept {
  pushValue(LogElement{LogType::CHAR, {.c = value}});
}

auto Logger::pushValue(const int value) noexcept {
  pushValue(LogElement{LogType::INTEGER, {.i = value}});
}

auto Logger::pushValue(const long value) noexcept {
  pushValue(LogElement{LogType::LONG_INTEGER, {.l = value}});
}

auto Logger::pushValue(const long long value) noexcept {
  pushValue(LogElement{LogType::LONG_LONG_INTEGER, {.ll = value}});
}

auto Logger::pushValue(const unsigned value) noexcept {
  pushValue(LogElement{LogType::UNSIGNED_INTEGER, {.u = value}});
}

auto Logger::pushValue(const unsigned long value) noexcept {
  pushValue(LogElement{LogType::UNSIGNED_LONG_INTEGER, {.ul = value}});
}

auto Logger::pushValue(const unsigned long long value) noexcept {
  pushValue(LogElement{LogType::UNSIGNED_LONG_LONG_INTEGER, {.ull = value}});
}

auto Logger::pushValue(const float value) noexcept {
  pushValue(LogElement{LogType::FLOAT, {.f = value}});
}

auto Logger::pushValue(const double value) noexcept {
  pushValue(LogElement{LogType::DOUBLE, {.d = value}});
}

auto Logger::pushValue(const char *value) noexcept {
  while (*value) {
    pushValue(*value);
    ++value;
  }
}

auto Logger::pushValue(const std::string &value) noexcept {
  pushValue(value.c_str());
}

// step 6:
// 这个接口支持像 log("% %", 42, "ok") 一样的顺序替换。通过递归模板逐个消耗参数，遇到 % 就写入当前值，遇到 %% 写一个 %。
template <typename T, typename... A>
auto Logger::log(const char *s, const T &value, A... args) noexcept {
  while (*s) {
    if (*s == '%') {
      if (UNLIKELY(*(s + 1) == '%')) {
        ++s;
      } else {
        pushValue(value);
        log(s + 1, args...);
        return;
      }
    }
    pushValue(*s++);
  }
  FATAL("extra arguments provided to log()");
}

auto Logger::log(const char *s) noexcept {
  while (*s) {
    if (*s == '%') {
      if (UNLIKELY(*(s + 1) == '%')) {
        ++s;
      } else {
        FATAL("missing arguments to log()");
      }
    }
    pushValue(*s++);
  }
}
}